/******************************************************************************
FileName: core.hpp
Version: 1
Time: 2019-05-19 16:28:01
Description: 
******************************************************************************/
#ifndef __CORE_HPP_
#define __CORE_HPP_

#include <string>
#include <vector>
#include <map>
#include <list>
#include "GL/glew.h"
#include "glm/glm.hpp"

namespace freecraft
{

class AABB
{
public:
    glm::vec3                           min;
    glm::vec3                           max;
	std::vector<glm::vec3>              vectices;
	AABB();
    AABB(const std::vector<glm::vec3> &vectices);
    AABB(std::vector<glm::vec3> &&vectices);
    void updateMinMax();
    void updateMinMax(const std::vector<glm::vec3> &vectices);
    void updateMinMax(std::vector<glm::vec3> &&vectices);
};

class Ray
{
public:
    glm::vec3                           pos;
    glm::vec3                           dir;
    Ray(glm::vec3 &pos, glm::vec3 &dir);
    bool rayCheck(const AABB &aabb, glm::vec3 &hit);
	bool rayCheck(glm::vec3 min, glm::vec3 max, glm::vec3& hit);
    static bool rayCheck(Ray &ray, const AABB &aabb, glm::vec3 &hit);
	static bool rayCheck(Ray& ray, glm::vec3 min, glm::vec3 max, glm::vec3& hit);

private:
	bool tCalc(float min, float max, float pos, float dir, float& tmin, float &tmax);
};

enum class ElementId:unsigned int
{
    AIR,
    BASE,
    STAIR,
    COUNT
};

enum class ObjectId: unsigned int
{
    BASE,
    STAIR,
    COUNT
};

#if 0
class Loader
{
public:
    // static bool objLoader(std::string &objName, BaseCube &obj);
};
#endif

class PacketVertex
{
public:
    glm::vec3                           position;
    glm::vec2                           uv;
    glm::vec3                           normal;
    bool operator<(const PacketVertex &that) const;
    bool operator<(PacketVertex &&that) const;
};

class Object
{
public:
    std::vector<glm::vec3>              vectices;
    std::vector<glm::vec2>              uvs;
    std::vector<glm::vec3>              normals;
    std::vector<unsigned short>         indices;
    GLuint                              vao;
    AABB                                aabb;
    Object();
    void free();
    static bool loadObject(std::string &path, Object &obj);
    static bool findSimilarVertexIndex(
        PacketVertex &packed, 
        std::map<PacketVertex, unsigned short> &indices, 
        unsigned short &index);
private:
    float *makeVertexBufferData();
};

class Texture
{
public:
    GLuint                              id;
    int                                 width;
    int                                 height;
    int                                 colorChannels;
    static bool loadTexture(const std::string &path, Texture &texture);
};

class Element
{
public:
    ObjectId                            objectId;
    ElementId                           elementId;
    Element();
    Element(ElementId id);
};

class Chunk
{
public:
    static const int                    CHUNK_SIZE = 16;
    static const std::string            CHUNK_SAVE_PATH;
    Element                             elements[CHUNK_SIZE][CHUNK_SIZE][CHUNK_SIZE];
    static std::list<Chunk*>            chunkList;
    glm::ivec3                          position;
    Chunk();
    Chunk(const glm::ivec3 &pos);
    static bool isSurrounded(const glm::ivec3 &pos);
    static bool isSurrounded(int x, int y, int z);
    static bool getElement(const glm::ivec3 &pos, Element &element);
    static bool getElement(int x, int y, int z, Element &element);
    static Element *getElement(const glm::ivec3 &pos);
    static bool isSolidElement(Element &element);
    static bool isSolidElement(int x, int y, int z);
    static Chunk *getChunk(const glm::ivec3 &pos);
    static bool loadChunk(const glm::ivec3 &pos, Chunk &chunk);
    static bool generateChunk(Chunk &chunk);
    static void saveChunk(Chunk &chunk);
    static void formatChunkPath(const glm::ivec3 &pos, std::string &path);
    static bool checkElementPos(const glm::ivec3 &pos);
};

class Resource
{
public:
    static const std::string            elementNames[(unsigned int)ElementId::COUNT];
    static const std::string            objectNames[(unsigned int)ObjectId::COUNT];
    static Object                       objects[(unsigned int)ObjectId::COUNT];
    static Resource* getInstance();
    static const Object &getObject(ObjectId id);

private:
    static Resource                     *instance;
    Resource();
    void init();
};

class Shader
{
public:
    GLuint                              id;
    Shader(const char* vspath, const char* fspath);
    void use();
    void setMatrix(const std::string &name, const glm::mat4 &m);
    void setInt(const std::string &name, int value);
};

}
#endif //__CORE_HPP_
